#include "stdafx.h"
#include "codegenapp.h"
#include "dentityfactory.h"
#include "dentitystructure.h"
#include "DDocument.h"
#include "dcreateclassdlg.h"
#include "dmanageclassdlg.h"
#include "dvisitorserializer.h"

#include "commandsur.h"

using namespace Commands;

CodeGenApp::CodeGenApp(QWidget *parent, Qt::WFlags flags)
	: QMainWindow(parent, flags)
	, m_pSelectedItem(0)
{
	ui.setupUi(this);
	ui.classView->setHeaderLabel("Classes");

	m_pDoc = new DDocument;
	QObject::connect(m_pDoc, SIGNAL(classAdded(DClassEntity*)), this, SLOT(onClassAdded(DClassEntity*)));
	QObject::connect(m_pDoc, SIGNAL(classRemoved(DClassEntity*)), this, SLOT(onClassRemoved(DClassEntity*)));

	m_pUndoStack = new QUndoStack;

	DEntityStructure::instance().init(ui.classView);

	ui.classView->setContextMenuPolicy(Qt::CustomContextMenu);
	QObject::connect(ui.classView, SIGNAL(customContextMenuRequested(const QPoint&)), 
		this, SLOT(onContextMenuRequest(const QPoint&)));

	QObject::connect(ui.classView, SIGNAL(itemDoubleClicked(QTreeWidgetItem *, int)), 
		this, SLOT(onItemDoubleClicked(QTreeWidgetItem *, int)));

	QAction* pUndoAction = m_pUndoStack->createUndoAction(this, "Undo");
	QAction* pRedoAction = m_pUndoStack->createRedoAction(this, "Redo");

	ui.mainToolBar->addAction(pUndoAction);
	ui.mainToolBar->addAction(pRedoAction);

	DGraphicsView* pGV = qobject_cast<DGraphicsView*>(ui.graphicsView);
	pGV->setDocument(m_pDoc);
}

CodeGenApp::~CodeGenApp()
{

}

void CodeGenApp::onContextMenuRequest(const QPoint& pos)
{
	QTreeWidgetItem * pItem = ui.classView->itemAt(pos.x(), pos.y());

	if(pItem)
	{
		m_pSelectedItem = pItem;
		QMenu* pMenu = new QMenu;

		QAction* pManageClass = pMenu->addAction(QString("Manage '%1' Class...").arg(m_pSelectedItem->text(0)));

		pMenu->addSeparator();

		if(pItem == ui.classView->topLevelItem(0))
		{
			// add data class
			QAction* pCreateDataAction = pMenu->addAction("Create Data Class...");
			QObject::connect(pCreateDataAction, SIGNAL(triggered()), this, SLOT(onCreateDataClass()));
		}
		else
		{
			// add BL class
			QAction* pCreateBLAction = pMenu->addAction("Create Business Logic Class...");
			QObject::connect(pCreateBLAction, SIGNAL(triggered()), this, SLOT(onCreateBLClass()));
		}

		pMenu->addSeparator();

		QAction* pRemoveClass = pMenu->addAction("Remove Class...");
		QObject::connect(pRemoveClass, SIGNAL(triggered()), this, SLOT(onRemoveClass()));

		pMenu->exec(ui.classView->mapToGlobal(pos));
	}
	else if(!ui.classView->topLevelItem(0))
	{
		// if tree is empty show context menu for creating base clase item
		QMenu* pMenu = new QMenu;
		QAction* pCreateBaseAction = pMenu->addAction("Create Base Class...");
		QObject::connect(pCreateBaseAction, SIGNAL(triggered()), this, SLOT(onCreateBaseClass()));

		QAction* pCreateEnumerationAction = pMenu->addAction("Create Enumeration...");

		pMenu->exec(ui.classView->mapToGlobal(pos));
	}
	else
	{
		// add create enum option
		QMenu* pMenu = new QMenu;
		QAction* pCreateEnumerationAction = pMenu->addAction("Create Enumeration...");
		pMenu->exec(ui.classView->mapToGlobal(pos));
	}

	m_pSelectedItem = 0;
}

void CodeGenApp::onItemDoubleClicked(QTreeWidgetItem * item, int column)
{
	DAbstractEntity* pEntity = DEntityStructure::instance().classByItem(item);

	if(!pEntity)
	{
		// do nfn
	}
	else if(pEntity->entityType() == eClass)
	{
		DManageClassDlg dlg(qobject_cast<DClassEntity*>(pEntity));

		// give dialog all the data it needs

		if(dlg.exec() == QDialog::Accepted)
		{

		}
	}
	else if(pEntity->entityType() == eEnumeration)
	{

	}
}

void CodeGenApp::onCreateBaseClass()
{
	DCreateClassDlg dlg;
	dlg.ui.editClassName->setText("Enter base class name");
	dlg.ui.editClassName->selectAll();
	dlg.ui.editBaseClassName->setText("");
	dlg.update();
	if(dlg.exec() == QDialog::Accepted)
	{
		Access inhType;
		if(dlg.ui.radioPublic->isChecked())
		{
			inhType = ePublic;
		}
		else if(dlg.ui.radioProtected->isChecked())
		{
			inhType = eProtected;
		}
		else
		{
			inhType = ePrivate;
		}

		DAddClassCommand* pCommand = new DAddClassCommand(eBase, dlg.ui.editClassName->text(), inhType
			,m_pDoc, 0);

		m_pUndoStack->push(pCommand);
	}
}

void CodeGenApp::onCreateDataClass()
{
	DClassEntity* pBaseClass = DEntityStructure::instance().baseClass();

	if(!pBaseClass)
	{
		// this should not happend according to app flow
		Q_ASSERT(false);
		return;
	}

	DCreateClassDlg dlg;
	dlg.ui.editClassName->setText("Enter data class name");
	dlg.ui.editClassName->selectAll();
	dlg.ui.editBaseClassName->setText(pBaseClass->name());
	dlg.update();
	if(dlg.exec() == QDialog::Accepted)
	{
		// create item...
		Access inhType;
		if(dlg.ui.radioPublic->isChecked())
		{
			inhType = ePublic;
		}
		else if(dlg.ui.radioProtected->isChecked())
		{
			inhType = eProtected;
		}
		else
		{
			inhType = ePrivate;
		}

		DAddClassCommand* pCommand = new DAddClassCommand(eData, dlg.ui.editClassName->text(), inhType
			,m_pDoc, pBaseClass);

		m_pUndoStack->push(pCommand);
	}
}

void CodeGenApp::onCreateBLClass()
{
	DClassEntity* pBaseClass = DEntityStructure::instance().classByItem(m_pSelectedItem);

	if(!pBaseClass)
	{
		// this should not happend according to app flow
		Q_ASSERT(false);
		return;
	}

	DCreateClassDlg dlg;
	dlg.ui.editClassName->setText("Enter business logic class name");
	dlg.ui.editClassName->selectAll();
	dlg.ui.editBaseClassName->setText(pBaseClass->name());
	dlg.update();
	if(dlg.exec() == QDialog::Accepted)
	{
		Access inhType;
		if(dlg.ui.radioPublic->isChecked())
		{
			inhType = ePublic;
		}
		else if(dlg.ui.radioProtected->isChecked())
		{
			inhType = eProtected;
		}
		else
		{
			inhType = ePrivate;
		}
		// create item...
		DAddClassCommand* pCommand = new DAddClassCommand(eBusinessLogic, dlg.ui.editClassName->text(), inhType
			,m_pDoc, pBaseClass);

		m_pUndoStack->push(pCommand);
	}
}

void CodeGenApp::onRemoveClass()
{
	if(QMessageBox::question(this, "Remove Class", "Are you sure?",
		QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes)
	{
		DRemoveClassCommand* pCommand = 
			new DRemoveClassCommand(DEntityStructure::instance().classByItem(m_pSelectedItem), m_pDoc);
		m_pUndoStack->push(pCommand);
		m_pSelectedItem = 0;
	}
}

QIcon CodeGenApp::createIcon(ClassType eType)
{
	QPixmap pm(16, 16);
	QPainter painter(&pm);
	painter.fillRect(pm.rect(), QBrush(Qt::white));

	QColor color;
	switch(eType)
	{
		case eBase: color = Qt::red; break;
		case eData: color = Qt::blue; break;
		case eBusinessLogic: color = Qt::green; break;
	}

	painter.setPen(Qt::NoPen);
	QBrush brush(color);
	QRadialGradient gradient(QPointF(8, 8), 8, QPointF(6,6));
	gradient.setColorAt(1, color);
	gradient.setColorAt(0, Qt::white);

	painter.setRenderHint(QPainter::Antialiasing);
	painter.setBrush(QBrush(gradient));
	painter.drawEllipse(pm.rect().adjusted(2, 2, -2, -2));
	QIcon icon(pm);
	return icon;
}

void CodeGenApp::onClassAdded(DClassEntity* pClass)
{
	QTreeWidgetItem* pItem = new QTreeWidgetItem;
	pItem->setText(0, pClass->name());
	pItem->setIcon(0, createIcon(pClass->classType()));
	QVariant val;
	val.setValue(pClass);
	pItem->setData(0, Qt::UserRole, val);

	if(const DClassEntity* pParentClass = pClass->baseClass())
	{
		// find base class item and add this item as child
		// and expand
		QTreeWidgetItem* pParentItem = DEntityStructure::instance().itemByClass(pParentClass);
		pParentItem->addChild(pItem);

		pParentItem->setExpanded(true);
	}
	else
	{
		ui.classView->addTopLevelItem(pItem);
	}

	if(pClass->subclasses().size())
	{
		QList<DClassEntity*> subclasses = pClass->subclasses();
		foreach(DClassEntity* pSubclass, subclasses)
		{
			onClassAdded(pSubclass);
		}
	}
}

void CodeGenApp::onClassRemoved(DClassEntity* pClass)
{
	QTreeWidgetItem* pItem = DEntityStructure::instance().itemByClass(pClass);

	if(pItem)
	{
		if(QTreeWidgetItem* pParentItem = pItem->parent())
		{
			pParentItem->removeChild(pItem);
		}
		else
		{
			// top level item, remove all
			ui.classView->clear();
		}
	}
}

void CodeGenApp::on_actionNew_triggered()
{
	
}

void CodeGenApp::on_actionLoad_triggered()
{

}

void CodeGenApp::on_actionSave_triggered()
{
	Q_ASSERT(m_pDoc);

	QString strPath = "test.xml";
	// get file path

	DVisitorSerializer visitor(strPath);
	QList<DClassEntity*> classes = m_pDoc->classes();

	foreach(DClassEntity* pClass, classes)
	{
		QUuid guid = pClass->guid();
		visitor.visit(pClass);
	}
}

void CodeGenApp::on_actionExit_triggered()
{

}